<?php
/**
 * Form.php
 *
 * @category    Madoqua
 * @package     Form
 */

/** base class */
require_once 'Zend/Form.php';

/** subforms */
require_once 'Zend/Form/SubForm.php';

/** redirector */
require_once 'Madoqua/Redirector.php';

/**
 * Madoqua_Form
 *
 * @category    Madoqua
 * @package     Form
 */
class Madoqua_Form extends Zend_Form
{
    /**
     * constructor
     */
    public function __construct($options = null)
    {
        parent::__construct($options);

        $this->addElementPrefixPath('Madoqua_Validate', 'Madoqua/Validate', 'validate');
        //add Madoqua validaters to the plugin loader

        if (!isset($options['donttrack'])) {
            $this->addElement('hidden', '_formname', array(
            'value' => get_class($this)
            ));
            //track the form name, for use in isPosted()
        }
        
        $action = Madoqua_Application::getFront()->getRequest()->getRequestUri();
        $this->setAction($action);
        //always set an action
        //safari bitches if you don't set one and have an action=""
    }

    /**
     * has this form been posted?
     *
     * will not start validation loop
     *
     * @return bool
     */
    public function isPosted()
    {
        $request = Madoqua_Application::getFront()->getRequest();
        return ($request->getPost('_formname') == get_class($this));
    }

    /**
     * check whether the form has been prg-ed
     *
     * also performs the redirect logic
     *
     * @param string $url url to redirect to
     * @return bool
     * @todo fix this, so the url is taken into account
     */
    public function isPrg($url = null, $checkFormName = false)
    {
        $request = Madoqua_Application::getFront()->getRequest();
        $prgParam = $request->getParam('prg');
        //prg param from the request

        $session = Madoqua_Application::getSession();
        //the session namespace

        if ($this->isPosted()) {
            //we have data from a post

            if (empty($session->prg)) {
                $session->prg = array();
            }
            //create a prg container in the session namespace

            if ($prgParam == null) {
                $prgKey = $this->getPrgKey();
                //find a new key to use
            } else {
                $prgKey = (int) $prgParam;
                //use the original key (but update the values)
            }
            
            $session->prg[$prgKey] = Madoqua_Application::getFront()->getRequest()->getPost();
            //remember the POST data in the session

            Madoqua_Redirector::redirectAndExit(array('prg' => $prgKey));
            //redirect and halt execution
        }

        if ($prgParam == null) {
            //we haven't redirected yet... ahw :(
            return false;
            //no post and no redirect means... no prg!
        } else {
            //we have been redirected \ó/

            $prgParam = (int) $prgParam;
            //cast to int (because ints are nice)

            if (!isset($session->prg[$prgParam])) {
                return false;
                //form has already gone through the PRG cycle
            } elseif($checkFormName && $session->prg[$prgParam]['_formname'] != get_class($this)) {
                //form name doesn't match
                return false;
            }

            return true;
            //oooh baby, we totally did the prg...
            //how was it for you?
        }
    }

    /**
     * PRG version of isValid()
     *
     * @return bool
     */
    public function isValidPrg() {
        return $this->isValid($this->getPrgData());
    }

    /**
     * get prg data (to be used with isValid())
     *
     * @return array
     */
    public function getPrgData()
    {
        $key = $this->getExistingPrgKey();

        return Madoqua_Application::getSession()->prg[$key];
        //return the right prg data from the session
    }

    /**
     * clean prg data (so prg won't repeatedly be true)
     *
     * @return void
     */
    public function cleanPrgData()
    {
        $key = $this->getExistingPrgKey();

        unset(Madoqua_Application::getSession()->prg[$key]);
    }

    /**
     * get a PRG key from the session
     *
     * @return int
     */
    protected function getExistingPrgKey()
    {
        $request = Madoqua_Application::getFront()->getRequest();
        $prgParam = $request->getParam('prg');
        //prg param from the request

        if ($prgParam === null) {
            throw new Exception('PRG param was null, no prg has happened');
        }

        return (int) $prgParam;
    }

    /**
     * get a prg key
     *
     * @return int
     */
    protected function getPrgKey()
    {
        $session = Madoqua_Application::getSession();
        //central madoqua session

        if (empty($session->form_prg_key)) {
            $session->form_prg_key = 0;
        }
        //create a counter for the prg key

        $session->form_prg_key = $session->form_prg_key + 1;
        //go up... up up up! never down :x
        //the first key is 1, not 0, because people don't know how to count :x

        return $session->form_prg_key;
    }

    /**
     * get values matching a model
     *
     * @param Zend_Db_Table $model
     */
    public function getModelValues($model)
    {
        $values = array();

        $fields = $model->info('cols');
        //the fields that exist in the model

        foreach($fields as $field) {
            //for each field in the model
            if ($this->getElement($field) != null) {
                //the element matching the field exists! \ó/
                $values[$field] = $this->getValue($field);
            }
        }
        
        $subforms = $this->getSubForms();
        foreach($subforms as $subform) {
            //assuming one level of subforms
            //FIXME doesn't have to be the case of course
            
            foreach($fields as $field) {
                //for each field in the model
                if ($subform->getElement($field) != null) {
                    //the element matching the field exists! \ó/
                    $values[$field] = $subform->getValue($field);
                }
            }
        }

        return $values;
    }

    /**
     * get form values
     *
     * @param array $exclude
     * @param bool $includeSubmits
     * @return array
     */
    public function getValues($exclude = array(), $includeSubmits = false)
    {
        $values = parent::getValues();

        foreach ($exclude as $dont) {
            unset($values[$dont]);
        }
        //exclude array

        if ($includeSubmits !== true) {
            foreach ($this->getElements() as $name => $element) {
                if ($element->getType() == 'Zend_Form_Element_Submit') {
                    unset($values[$name]);
                }
            }
            //don't include values of submits
        }
        
        unset($values['_formname']);
        //remove form name (if it exists)
        
        return $values;
    }

    /**
     * set the action based on a route
     *
     * @param array $params
     * @param string $routeName
     * @param bool $reset
     */
    public function setRouteAction($params = array(), $routeName = null, $reset = false)
    {
        $frontController = Madoqua_Application::getFront();
        //the FC

        $router = Madoqua_Application::getFront()->getRouter();
        //the router (assumes rewrite router)

        if ($reset && $routeName === null) {
            $routeName = 'default';
        }
        //hmm... this may lead to unexpected results
        //but so will getting the current route name

        if ($routeName == null) {
            try {
                $routeName = $router->getCurrentRouteName();
            } catch (Zend_Controller_Router_Exception $e) {
                $routeName = 'default';
            }
        }
        //requested routename/default

        $route = $router->getRoute($routeName);
        //the current route

        $action = rtrim($frontController->getBaseUrl(), '/') . '/';
        $action .= $route->assemble($params, $reset);

        $this->setAction($action);
    }
}